package com.tencent.qcloud.tuicore.util.kt.viewbinding

import android.app.Activity
import android.view.LayoutInflater
import android.view.View
import androidx.core.app.ComponentActivity
import androidx.fragment.app.Fragment
import androidx.lifecycle.LifecycleOwner
import androidx.viewbinding.ViewBinding
import me.luochenyxh.base.ext.viewbinding.ext.addObserverDestroyed
import me.luochenyxh.base.ext.viewbinding.ext.inflateMethod
import me.luochenyxh.base.ext.viewbinding.ext.inflateMethodParent
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty

/**
 * 作者 : yuan_xh
 * 时间 : 2022/7/26 9:19
 * 说明 :
 */

/**
 * ComponentActivity 中使用
 * @param isRef 默认false 如果true activity可以不用写setContentView
 * 使用： val mBinding by viewBinding<xxxBinding>()
 * */
inline fun <reified T : ViewBinding> ComponentActivity.viewBinding(isRef: Boolean = false) =
    ActivityViewBindingDelegate(T::class.java, this, isRef)

/**
 *  Fragment 中使用
 * 使用：val mBinding by viewBinding<xxxBinding>()
 * */
inline fun <reified T : ViewBinding> Fragment.viewBinding() =
    FragmentViewBindingDelegate(T::class.java, this)

/**
 *  View 中使用
 * 使用：val mBinding by viewBinding<xxxBinding>()
 *
 * */
inline fun <reified T : ViewBinding> View.viewBinding() =
    ViewViewBindingDelegate(T::class.java, this, false)


class ActivityViewBindingDelegate<T : ViewBinding>(
    classes: Class<T>,
    val act: Activity,
    private val isRef: Boolean = false
) : ReadOnlyProperty<Activity, T> {
    init {
        when (act) {
            is LifecycleOwner -> act.lifecycle.addObserverDestroyed { destroyed() }
        }
    }

    private val layoutInflater = classes.inflateMethod()
    private var viewBinding: T? = null

    override fun getValue(thisRef: Activity, property: KProperty<*>): T {
        return viewBinding?.run { this } ?: let {
            val bind = layoutInflater.invoke(null, thisRef.layoutInflater) as T
            if (isRef) {
                thisRef.setContentView(bind.root)
            }
            bind.apply { viewBinding = this }
        }
    }

    private fun destroyed() {
        viewBinding = null
    }
}

class FragmentViewBindingDelegate<T : ViewBinding>(
    classes: Class<T>,
    fragment: Fragment
) : ReadOnlyProperty<Fragment, T> {

    var viewBinding: T? = null
    val bindView = classes.inflateMethod()

    init {
        fragment.lifecycle.addObserverDestroyed { destroyed() }
    }

    override fun getValue(thisRef: Fragment, property: KProperty<*>): T {
        return viewBinding?.run {
            return this
        } ?: let {
            val bind = bindView.invoke(null, thisRef.layoutInflater) as T
            return bind.apply { viewBinding = this }
        }
    }

    private fun destroyed() {
        viewBinding = null
    }
}


class ViewViewBindingDelegate<T : ViewBinding>(
    classes: Class<T>,
    val act: View,
    val bind: Boolean = true
) : ReadOnlyProperty<View, T> {
    init {
        when {
            act is LifecycleOwner -> act.lifecycle.addObserverDestroyed { destroyed() }
            act.context is LifecycleOwner -> (act.context as LifecycleOwner).lifecycle.addObserverDestroyed { destroyed() }
        }
    }

    private val layoutInflaterParent = classes.inflateMethodParent()
    private val layoutInflater = classes.inflateMethod()
    private var viewBinding: T? = null

    override fun getValue(parent: View, property: KProperty<*>): T {
        return viewBinding?.run { this } ?: let {
            val bind = when (bind) {
                true -> layoutInflaterParent.invoke(
                    null,
                    LayoutInflater.from(parent.context),
                    parent,
                    true
                ) as T
                else -> layoutInflater.invoke(null, LayoutInflater.from(parent.context)) as T
            }
            bind.apply { viewBinding = this }
        }
    }

    private fun destroyed() {
        viewBinding = null
    }
}
